package com.kexilo.system.auth.service.impl;

import com.kexilo.core.common.utils.uuid.IdUtils;
import com.kexilo.core.security.domain.RefreshToken;
import com.kexilo.core.security.mapper.RefreshTokenMapper;
import com.kexilo.system.auth.service.IRefreshTokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 刷新令牌服务实现
 *
 * @author Kexilo
 */
@Service
public class RefreshTokenServiceImpl implements IRefreshTokenService {
    
    private static final Logger log = LoggerFactory.getLogger(RefreshTokenServiceImpl.class);

    @Autowired
    private RefreshTokenMapper refreshTokenMapper;

    /** 刷新令牌过期时间（天） */
    @Value("${kexilo.token.refresh.expireTime:30}")
    private int refreshTokenExpireDays;

    /** 每用户最大令牌数 */
    @Value("${kexilo.token.refresh.maxTokensPerUser:5}")
    private int maxTokensPerUser;

    @Override
    @Transactional
    public RefreshToken createRefreshToken(Long userId, String accessToken, String deviceId, String userAgent, String ipAddress) {
        try {
            // 检查并清理该用户超限的令牌
            cleanExcessTokens(userId, maxTokensPerUser);

            // 如果是同一设备，先撤销旧的令牌
            if (deviceId != null) {
                RefreshToken existingToken = refreshTokenMapper.selectByUserIdAndDeviceId(userId, deviceId);
                if (existingToken != null) {
                    refreshTokenMapper.revokeByRefreshToken(existingToken.getRefreshToken());
                }
            }

            // 生成新的刷新令牌
            String refreshTokenValue = generateRefreshToken();
            LocalDateTime expireTime = LocalDateTime.now().plusDays(refreshTokenExpireDays);

            RefreshToken refreshToken = new RefreshToken(userId, refreshTokenValue, accessToken, expireTime);
            refreshToken.setDeviceId(deviceId);
            refreshToken.setUserAgent(userAgent);
            refreshToken.setIpAddress(ipAddress);

            refreshTokenMapper.insert(refreshToken);
            
            log.info("Created refresh token for user: {}, device: {}", userId, deviceId);
            return refreshToken;

        } catch (Exception e) {
            log.error("Failed to create refresh token for user: {}", userId, e);
            throw new RuntimeException("创建刷新令牌失败", e);
        }
    }

    @Override
    public RefreshToken findByRefreshToken(String refreshToken) {
        try {
            return refreshTokenMapper.selectByRefreshToken(refreshToken);
        } catch (Exception e) {
            log.error("Failed to find refresh token: {}", refreshToken, e);
            return null;
        }
    }

    @Override
    public RefreshToken findByAccessToken(String accessToken) {
        try {
            return refreshTokenMapper.selectByAccessToken(accessToken);
        } catch (Exception e) {
            log.error("Failed to find refresh token by access token", e);
            return null;
        }
    }

    @Override
    public boolean validateRefreshToken(String refreshToken) {
        try {
            RefreshToken token = refreshTokenMapper.selectByRefreshToken(refreshToken);
            if (token == null) {
                log.warn("Refresh token not found: {}", refreshToken);
                return false;
            }

            if (!token.isValid()) {
                log.warn("Invalid refresh token: expired={}, used={}, revoked={}", 
                        token.isExpired(), token.isUsed(), token.isRevoked());
                return false;
            }

            return true;
        } catch (Exception e) {
            log.error("Failed to validate refresh token", e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean refreshAccessToken(String refreshToken, String newAccessToken) {
        try {
            RefreshToken token = refreshTokenMapper.selectByRefreshToken(refreshToken);
            if (token == null || !token.isValid()) {
                return false;
            }

            // 更新访问令牌和使用时间
            token.setAccessToken(newAccessToken);
            token.setLastUsedTime(LocalDateTime.now());
            token.setUseCount(token.getUseCount() + 1);

            refreshTokenMapper.updateById(token);
            
            log.info("Refreshed access token for user: {}", token.getUserId());
            return true;

        } catch (Exception e) {
            log.error("Failed to refresh access token", e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean revokeRefreshToken(String refreshToken) {
        try {
            int result = refreshTokenMapper.revokeByRefreshToken(refreshToken);
            boolean success = result > 0;
            
            if (success) {
                log.info("Revoked refresh token: {}", refreshToken);
            } else {
                log.warn("Failed to revoke refresh token: {}", refreshToken);
            }
            
            return success;
        } catch (Exception e) {
            log.error("Failed to revoke refresh token", e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean revokeAllUserTokens(Long userId) {
        try {
            int result = refreshTokenMapper.revokeAllByUserId(userId);
            log.info("Revoked {} tokens for user: {}", result, userId);
            return result >= 0;
        } catch (Exception e) {
            log.error("Failed to revoke all tokens for user: {}", userId, e);
            return false;
        }
    }

    @Override
    public List<RefreshToken> getUserValidTokens(Long userId) {
        try {
            return refreshTokenMapper.selectValidByUserId(userId);
        } catch (Exception e) {
            log.error("Failed to get valid tokens for user: {}", userId, e);
            return List.of();
        }
    }

    @Override
    public RefreshToken findByUserIdAndDeviceId(Long userId, String deviceId) {
        try {
            return refreshTokenMapper.selectByUserIdAndDeviceId(userId, deviceId);
        } catch (Exception e) {
            log.error("Failed to find token for user: {}, device: {}", userId, deviceId, e);
            return null;
        }
    }

    @Override
    @Transactional
    public int cleanExpiredTokens() {
        try {
            int count = refreshTokenMapper.deleteExpiredTokens();
            if (count > 0) {
                log.info("Cleaned {} expired refresh tokens", count);
            }
            return count;
        } catch (Exception e) {
            log.error("Failed to clean expired tokens", e);
            return 0;
        }
    }

    @Override
    @Transactional
    public int cleanUsedTokens(int retentionDays) {
        try {
            LocalDateTime beforeTime = LocalDateTime.now().minusDays(retentionDays);
            int count = refreshTokenMapper.deleteUsedTokensBefore(beforeTime);
            if (count > 0) {
                log.info("Cleaned {} used refresh tokens older than {} days", count, retentionDays);
            }
            return count;
        } catch (Exception e) {
            log.error("Failed to clean used tokens", e);
            return 0;
        }
    }

    @Override
    @Transactional
    public void cleanExcessTokens(Long userId, int maxTokensPerUser) {
        try {
            List<RefreshToken> userTokens = refreshTokenMapper.selectValidByUserId(userId);
            if (userTokens.size() >= maxTokensPerUser) {
                // 保留最新的几个，撤销多余的旧令牌
                for (int i = maxTokensPerUser - 1; i < userTokens.size(); i++) {
                    refreshTokenMapper.revokeByRefreshToken(userTokens.get(i).getRefreshToken());
                }
                log.info("Cleaned excess tokens for user: {}, removed: {}", 
                        userId, userTokens.size() - maxTokensPerUser + 1);
            }
        } catch (Exception e) {
            log.error("Failed to clean excess tokens for user: {}", userId, e);
        }
    }

    /**
     * 生成刷新令牌
     */
    private String generateRefreshToken() {
        return "RT_" + IdUtils.fastUUID().replace("-", "");
    }
}
